package com.zhanglu.authentication.security;

import com.zhanglu.authentication.service.TokenService;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;

/**
 * @author zhanglu
 * @description
 * @date 2017/11/19 14:38
 */
@Component
@Slf4j
public class TokenProcessFilter extends BasicAuthenticationFilter {
    @Autowired
    private TokenUtil tokenUtil;
    @Autowired
    private TokenService tokenService;

    public TokenProcessFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        Cookie[] cookieArr =  request.getCookies();
        if (null == cookieArr || cookieArr.length <= 0) {
            log.debug("Cookie was not found,the url is {},method is {}", request.getRequestURL(),request.getMethod());
            super.doFilterInternal(request, response, chain);
            return;
        }

        String token = request.getHeader(tokenUtil.getCOOKIE_AUTH());
        if (Objects.isNull(token)) {
            log.debug("the header has no token!");
            token = Arrays.stream(cookieArr).filter(cookie -> tokenUtil.getCOOKIE_AUTH().equalsIgnoreCase(cookie.getName()))
                    .map(Cookie::getValue).findFirst().orElse(null);
            log.debug("doFilter get token from cookie is {}", token);
        }

        if (StringUtils.isBlank(token)) {
            log.debug("token was not found，this url is:{},{}", request.getRequestURL(), request.getMethod());
            super.doFilterInternal(request, response, chain);
            return;
        }

        try {
            AuthenticationUser authenticationUser = tokenUtil.parserToken(token);
            log.info("user name is {}, roles are {}, the expire date is {}", authenticationUser.getUsername(), authenticationUser.getAuthorities(), authenticationUser.getExpireDate());

            if (preExpiredDateValid(authenticationUser.getExpireDate())) { // 预处理过期，需要去鉴权刷新
                ResponseEntity responseEntity = tokenService.refreshToken(token);
                if (responseEntity.getStatusCodeValue() == 200) {
                    String newToken = responseEntity.getHeaders().get(tokenUtil.getCOOKIE_AUTH()).get(0);
                    tokenUtil.setCookie(response, newToken);
                    tokenUtil.setRequestCookie(request, newToken);
                } else {
                    tokenUtil.removeResponseAuthCookie(response);
                    tokenUtil.removeRequestAuthCookie(request);
                }
            }
        } catch (Exception e) {
            if (e instanceof ExpiredJwtException) {
                log.warn("the token is time out,please login!");
                tokenUtil.removeResponseAuthCookie(response);
                tokenUtil.removeRequestAuthCookie(request);
                super.doFilterInternal(request, response, chain);
                return;
            }
        }
        super.doFilterInternal(request, response, chain);
    }

    public boolean preExpiredDateValid(Date expireDate){ //本地预处理过期日期，提前时间判断。
        Calendar ca = Calendar.getInstance();
        ca.setTime(expireDate);
        ca.add(Calendar.MINUTE, -30);

        long now = new Date().getTime();
        long tokenTime = ca.getTimeInMillis();

        return now > tokenTime;
    }
}
